#ifndef __BASE_SESSION_HPP__
#define __BASE_SESSION_HPP__

#include "TaskPackage.hpp"
#include <boost/asio.hpp>
#include <queue>

using boost::asio::ip::tcp;
using namespace std;

class baseSession {
protected:
  boost::asio::io_context &io_context_;
  tcp::socket socket_;
  std::queue<csn::TaskPackage_ptr> &recv_pkg_que_;
  size_t MAX_QUE_LEN = 100;
  csn::pkgHeader wheader_;
  csn::pkgHeader rheader_;
  char *rbuf_ = nullptr;
  char *wbuf_ = nullptr;
  csn::TaskPackage_ptr wpkg_ = nullptr; // used for write package
  std::mutex mtx_;

public:
  baseSession(boost::asio::io_context &io_context,
              std::queue<csn::TaskPackage_ptr> &recv_pkg_que,
              tcp::socket socket)
      : io_context_(io_context), recv_pkg_que_(recv_pkg_que),
        socket_(std::move(socket)) {}
  ~baseSession(){};

protected:
  virtual void err_handler() { cout << "error_handler" << endl; };
  void do_read_header() {
    rbuf_ = new char[sizeof(csn::pkgHeader)];
    boost::asio::async_read(
        socket_, boost::asio::buffer(rbuf_, sizeof(csn::pkgHeader)),
        [this](boost::system::error_code ec, std::size_t) {
          if (!ec) {
            if (rbuf_) {
              memcpy((char *)&rheader_, rbuf_, sizeof(rheader_));
              delete[] rbuf_;
              do_read_payload();
            }
          } else {
            err_handler();
          }
        });
  }
  void do_read_payload() {
    rbuf_ = new char[rheader_.psize];
    boost::asio::async_read(
        socket_, boost::asio::buffer(rbuf_, rheader_.psize),
        [this](boost::system::error_code ec, std::size_t) {
          if (!ec) {
            if (rbuf_) {
              mtx_.lock();
              if (recv_pkg_que_.size() < MAX_QUE_LEN) {
                recv_pkg_que_.push(
                    std::make_shared<csn::TaskPackage>(rheader_, rbuf_));
              }
              mtx_.unlock();
              delete[] rbuf_;
              rbuf_ = nullptr;
              do_read_header();
            }
          } else {
            err_handler();
          }
        });
  }
  void do_write() {
    wheader_ = wpkg_->getPackageHeader();
    wbuf_ = new char[wheader_.psize + sizeof(wheader_)];
    memcpy(wbuf_, (char *)&wheader_, sizeof(wheader_));
    memcpy(wbuf_ + sizeof(wheader_), (char *)wpkg_->getPayload(),
           wheader_.psize);
    boost::asio::async_write(
        socket_, boost::asio::buffer(wbuf_, wheader_.psize + sizeof(wheader_)),
        [this](boost::system::error_code ec, std::size_t /*length*/) {
          if (!ec) {
            if (wbuf_) {
              delete[] wbuf_;
              wbuf_ = nullptr;
#ifdef DEBUG_INFO
              stringstream ss;
              ss << "send "
                 << static_cast<int>(wheader_.psize + sizeof(wheader_))
                 << "Bytes" << endl;
              cout << ss.str();
#endif // DEBUG_INFO
            }
          } else {
            err_handler();
          }
        });
  }
};

#endif /* __BASE_SESSION_HPP__ */
